<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 
<html>
<head>
<title>The Breakout game</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Qt4, tutorial, Breakout game, C++, 
programming, GUI, multiplatform, learn Qt4">
<meta name="description" content="In this part of the Qt4 tutorial, we create
a Breakout game">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

<script type="text/javascript">

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-5536206-1']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();

</script>

</head>

<body>

<div class="container2">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content2">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>The Breakout game</h1>


<p>
In this part of the Qt4 C++ programming tutorial we will create a simple 
Breakout game clone.
</p>

<div class="big_hor">
<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* big_horizontal */
google_ad_slot = "2904953388";
google_ad_width = 728;
google_ad_height = 90;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<p>
The Breakout is an arcade game developed by Atari Inc. The game was created 
in 1976. In this game, the player moves a paddle on the screen and bounces 
a ball/balls. The objective is to destroy bricks in the top of the window. 
</p>


<h2>The development</h2>

<p>
In our game, we have one paddle, one ball and 30 bricks. I have created an 
image for a ball, paddle and a brick in Inkscape. We use a timer to create 
a game cycle. We do not work with angles, we simply change directions. Top, 
bottom, left and right. I was inspired by the pybreakout game. It was 
developed in PyGame library by Nathan Dawson. 
</p>

<p>
The game is intentionally simple. There are no bonuses, levels, score. 
So that it is easier to understand. 
</p>

<p>
The Qt4 C++ programming library is developed for creating computer applications. 
Nevertheless, it can be used to create games as well. Developing a computer game 
is a great way to learn the Qt4 programming library. 
</p>

<div class="codehead">paddle.h</div>
<pre class="code">
#ifndef PADDLE_H
#define PADDLE_H

#include &lt;QImage&gt;
#include &lt;QRect&gt;

class Paddle
{

  public:
    Paddle();
    ~Paddle();

  public:
    void resetState();
    void moveLeft(int);
    void moveRight(int);
    QRect getRect();
    QImage &amp; getImage();

  private:
    QImage image;
    QRect rect;

};

#endif
</pre>

<p>
This is a header file for the paddle object. 
</p>

<div class="codehead">paddle.cpp</div>
<pre class="code">
#include "paddle.h"
#include &lt;iostream&gt;

Paddle::Paddle()
{
  image.load("paddle.png");

  rect = image.rect();
  resetState();
}

Paddle::~Paddle()
{
 std::cout &lt;&lt; ("Paddle deleted\n");
}

void Paddle::moveLeft(int left)
{
  if (rect.left() >= 2)
    rect.moveTo(left, rect.top());
}

void Paddle::moveRight(int right)
{
  if (rect.right() <= 298)
    rect.moveTo(right, rect.top());
}

void Paddle::resetState()
{
  rect.moveTo(200, 360);
}

QRect Paddle::getRect()
{
  return rect;
}

QImage &amp; Paddle::getImage()
{
  return image;
}
</pre>

<p>
The paddle can be moved to the right or to the left. 
</p>

<pre class="explanation">
Paddle::Paddle()
{
  image.load("paddle.png");

  rect = image.rect();
  resetState();
}
</pre>

<p>
In the constructor, we load our paddle image. Get the image rectangle and move the 
image to it's starting position. 
</p>

<pre class="explanation">
void Paddle::moveLeft(int left)
{
  if (rect.left() >= 2)
    rect.moveTo(left, rect.top());
}
</pre>

<p>
The <b>moveLeft()</b> method moves the rectangle to the left. 
</p>


<div class="codehead">brick.h</div>
<pre class="code">
#ifndef BRICK_H
#define BRICK_H

#include &lt;QImage&gt;
#include &lt;QRect&gt;

class Brick
{

  public:
    Brick(int, int);
    ~Brick();

  public:
    void resetState();
    bool isDestroyed();
    void setDestroyed(bool);
    QRect getRect();
    void setRect(QRect);
    QImage &amp; getImage();

  private:
    QImage image;
    QRect rect;
    int position;
    bool destroyed;

};

#endif
</pre>

<p>
This is the header file for the brick object. 
</p>

<div class="codehead">brick.cpp</div>
<pre class="code">
#include "brick.h"
#include &lt;iostream&gt;

Brick::Brick(int x, int y) 
{
  image.load("brick.png");
  destroyed = FALSE;
  rect = image.rect();
  rect.translate(x, y);
}

Brick::~Brick() {

   std::cout &lt;&lt; ("Brick deleted\n");
}

QRect Brick::getRect()
{
  return rect;
}

void Brick::setRect(QRect rct)
{
  rect = rct;
}

QImage &amp; Brick::getImage()
{
  return image;
}

bool Brick::isDestroyed()
{
  return destroyed;
}

void Brick::setDestroyed(bool destr)
{
  destroyed = destr;
}
</pre>


<pre class="explanation">
bool Brick::isDestroyed()
{
  return destroyed;
}
</pre>

<p>
The brick has a destroyed flag. If the destroyed flag is set, the brick is 
not drawn on the window. 
</p>

<div class="codehead">ball.h</div>
<pre class="code">
#ifndef BALL_H
#define BALL_H

#include &lt;QImage&gt;
#include &lt;QRect&gt;

class Ball
{

  public:
    Ball();
    ~Ball();

  public:
    void resetState();
    void moveBottom(int);
    void moveTop(int);
    void moveLeft(int);
    void moveRight(int);
    void autoMove();
    void setXDir(int);
    void setYDir(int);
    int getXDir();
    int getYDir();
    QRect getRect();
    QImage &amp; getImage();

  private:
    int angle;
    int speed;
    int xdir;
    int ydir;
    bool stuck;
    QImage image;
    QRect rect;

};

#endif
</pre>

<p>
The header file for the ball object.
</p>

<div class="codehead">ball.cpp</div>
<pre class="code">
#include "ball.h"
#include &lt;iostream&gt;

Ball::Ball()
{

  xdir = 1;
  ydir = -1;

  image.load("ball.png");

  rect = image.rect();
  resetState();

}

Ball::~Ball() {
  std::cout &lt;&lt; ("Ball deleted\n");
}


void Ball::autoMove()
{
  rect.translate(xdir, ydir);

  if (rect.left() == 0) {
    xdir = 1;
  }

  if (rect.right() == 300) {
    xdir = -1;
  }

  if (rect.top() == 0) {
    ydir = 1;
  }
}

void Ball::resetState() 
{
  rect.moveTo(230, 355);
}

void Ball::moveBottom(int bottom)
{
  rect.moveBottom(bottom);
}

void Ball::moveTop(int top)
{
  rect.moveTop(top);
}

void Ball::moveLeft(int left)
{
  rect.moveLeft(left);
}

void Ball::moveRight(int right)
{
  rect.moveRight(right);
}

void Ball::setXDir(int x)
{
  xdir = x;
}

void Ball::setYDir(int y)
{
  ydir = y;
}

int Ball::getXDir()
{
  return xdir;
}

int Ball::getYDir()
{
  return ydir;
}

QRect Ball::getRect()
{
  return rect;
}

QImage &amp; Ball::getImage()
{
  return image;
}
</pre>


<p>
The <b>autoMove()</b> method is called each game cycle to move 
the ball on the screen. If it hists the boudaries, the ball direction changes. 
</p>

<div class="codehead">breakout.h</div>
<pre class="code">
#ifndef BREAKOUT_H
#define BREAKOUT_H

#include "ball.h"
#include "brick.h"
#include "paddle.h"
#include &lt;QWidget&gt;
#include &lt;QKeyEvent&gt;

class Breakout : public QWidget
{
  Q_OBJECT

  public:
    Breakout(QWidget *parent = 0);
    ~Breakout();

  protected:
    void paintEvent(QPaintEvent *event);
    void timerEvent(QTimerEvent *event);
    void keyPressEvent(QKeyEvent *event);

    void startGame();
    void pauseGame();
    void stopGame();
    void victory();
    void checkCollision();

  private:
    int x;
    int timerId;
    Ball *ball;
    Paddle *paddle;
    Brick * bricks[30];
    bool gameOver;
    bool gameWon;
    bool gameStarted;
    bool paused;

};

#endif
</pre>

<p>
This is the header file for the breakout object. 
</p>

<pre class="explanation">
 int x;
 int timerId;
</pre>

<p>
The x value stores the current x position of the paddle. The timerId is used for 
identifying of the timer object. This is necessary, when we pause the game. 
</p>

<div class="codehead">breakout.cpp</div>
<pre class="code">
#include "breakout.h"
#include &lt;QPainter&gt;
#include &lt;QApplication&gt;

Breakout::Breakout(QWidget *parent)
    : QWidget(parent)
{

  x = 0;
  gameOver = FALSE;
  gameWon = FALSE;
  paused = FALSE;
  gameStarted = FALSE;
  ball = new Ball();
  paddle = new Paddle();


  int k = 0;
  for (int i=0; i<5; i++) {
    for (int j=0; j<6; j++) {
      bricks[k] = new Brick(j*40+30, i*10+50);
      k++; 
    }
  }
}

Breakout::~Breakout() {
 delete ball;
 delete paddle;
 for (int i=0; i<30; i++) {
   delete bricks[i];
 }
}

void Breakout::paintEvent(QPaintEvent *event)
{
  QPainter painter(this);

  if (gameOver) {
    QFont font("Courier", 15, QFont::DemiBold);
    QFontMetrics fm(font);
    int textWidth = fm.width("Game Over");

    painter.setFont(font);
    int h = height();
    int w = width();

    painter.translate(QPoint(w/2, h/2));
    painter.drawText(-textWidth/2, 0, "Game Over");
  }
  else if(gameWon) {
    QFont font("Courier", 15, QFont::DemiBold);
    QFontMetrics fm(font);
    int textWidth = fm.width("Victory");

    painter.setFont(font);
    int h = height();
    int w = width();

    painter.translate(QPoint(w/2, h/2));
    painter.drawText(-textWidth/2, 0, "Victory");
  }
  else {
    painter.drawImage(ball->getRect(), 
        ball->getImage());
    painter.drawImage(paddle->getRect(), 
        paddle->getImage());

    for (int i=0; i<30; i++) {
        if (!bricks[i]->isDestroyed()) 
          painter.drawImage(bricks[i]->getRect(), 
              bricks[i]->getImage());
    }
  }
}

void Breakout::timerEvent(QTimerEvent *event)
{
  ball->autoMove();
  checkCollision();
  repaint();
}



void Breakout::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_Left:
       {  
         int x = paddle->getRect().x();
        for (int i=1; i<=5; i++)
          paddle->moveLeft(x--);
        break;
       }
    case Qt::Key_Right:
        { 
          int x = paddle->getRect().x();
          for (int i=1; i<=5; i++)
            paddle->moveRight(x++);
        }
        break;
    case Qt::Key_P:
        { 
          pauseGame();
        }
        break;
    case Qt::Key_Space:
        {
          startGame();
        }
        break;
    case Qt::Key_Escape:
        {
          qApp->exit();
        }
        break;
    default:
        QWidget::keyPressEvent(event);
    }
}

void Breakout::startGame()
{ 
  if (!gameStarted) {
    ball->resetState();
    paddle->resetState();

    for (int i=0; i<30; i++) {
      bricks[i]->setDestroyed(FALSE);
    }
    gameOver = FALSE; 
    gameWon = FALSE; 
    gameStarted = TRUE;
    timerId = startTimer(10);  
  }
}

void Breakout::pauseGame()
{
  if (paused) {
    timerId = startTimer(10);
    paused = FALSE;
  } else {
    paused = TRUE;
    killTimer(timerId); 
   }
}

void Breakout::stopGame()
{
  killTimer(timerId);    
  gameOver = TRUE;      
  gameStarted = FALSE;
}

void Breakout::victory()
{
  killTimer(timerId);
  gameWon = TRUE;  
  gameStarted = FALSE;
}

void Breakout::checkCollision()
{

  if (ball->getRect().bottom() > 400)
    stopGame(); 

  for (int i=0, j=0; i<30; i++) {
    if (bricks[i]->isDestroyed()) {
      j++;
    }
    if (j==30) 
      victory();
  }

  if ((ball->getRect()).intersects(paddle->getRect())) {

    int paddleLPos = paddle->getRect().left();  
    int ballLPos = ball->getRect().left();   

    int first = paddleLPos + 8;
    int second = paddleLPos + 16;
    int third = paddleLPos + 24;
    int fourth = paddleLPos + 32;

    if (ballLPos < first) {
      ball->setXDir(-1);
      ball->setYDir(-1);
    }

    if (ballLPos >= first &amp;&amp; ballLPos < second) {
      ball->setXDir(-1);
      ball->setYDir(-1*ball->getYDir());
    }

    if (ballLPos >= second &amp;&amp; ballLPos < third) {
       ball->setXDir(0);
       ball->setYDir(-1);
    }

    if (ballLPos >= third &amp;&amp; ballLPos < fourth) {
       ball->setXDir(1);
       ball->setYDir(-1*ball->getYDir());
    }

    if (ballLPos > fourth) {
      ball->setXDir(1);
      ball->setYDir(-1);
    }
  }


  for (int i=0; i<30; i++) {
    if ((ball->getRect()).intersects(bricks[i]->getRect())) {

      int ballLeft = ball->getRect().left();  
      int ballHeight = ball->getRect().height(); 
      int ballWidth = ball->getRect().width();
      int ballTop = ball->getRect().top();  

      QPoint pointRight(ballLeft + ballWidth + 1, ballTop);
      QPoint pointLeft(ballLeft - 1, ballTop);  
      QPoint pointTop(ballLeft, ballTop -1);
      QPoint pointBottom(ballLeft, ballTop + ballHeight + 1);  

      if (!bricks[i]->isDestroyed()) {
        if(bricks[i]->getRect().contains(pointRight)) {
           ball->setXDir(-1);
        } 

        else if(bricks[i]->getRect().contains(pointLeft)) {
           ball->setXDir(1);
        } 

        if(bricks[i]->getRect().contains(pointTop)) {
           ball->setYDir(1);
        } 

        else if(bricks[i]->getRect().contains(pointBottom)) {
           ball->setYDir(-1);
        } 

        bricks[i]->setDestroyed(TRUE);
      }
    }
  }

}
</pre>

<p>
Here is the game logic. 
</p>


<pre class="explanation">
 int k = 0;
 for (int i=0; i<5; i++) {
   for (int j=0; j<6; j++) {
     bricks[k] = new Brick(j*40+30, i*10+50);
     k++; 
   }
 }
</pre>

<p>
In the constructor of the Breakout object, we create 30 bricks. 
</p>

<pre class="explanation">
 painter.drawImage(ball->getRect(), 
     ball->getImage());
 painter.drawImage(paddle->getRect(), 
     paddle->getImage());

 for (int i=0; i<30; i++) {
     if (!bricks[i]->isDestroyed()) 
       painter.drawImage(bricks[i]->getRect(), 
           bricks[i]->getImage());
 }
</pre>

<p>
If the game is not won or lost, we draw the ball, paddle and the bricks in the 
<b>paintEvent()</b>.
</p>

<pre class="explanation">
void Breakout::timerEvent(QTimerEvent *event)
{
  ball->autoMove();
  checkCollision();
  repaint();
}
</pre>

<p>
In the <b>timerEvent()</b>, we move the ball, check if the ball
collided with the paddle or a brick and generate a paint event. 
</p>

<pre class="explanation">
 case Qt::Key_Left:
    {  
      int x = paddle->getRect().x();
     for (int i=1; i<=5; i++)
       paddle->moveLeft(x--);
     break;
    }
</pre>

<p>
If we press the left cursor key, we call the <b>moveLeft()</b> method of the paddle object. 
We call the method five times, so that the movement is smooth. 
</p>

<pre class="explanation">
void Breakout::stopGame()
{
  killTimer(timerId);    
  gameOver = TRUE;      
  gameStarted = FALSE;
}
</pre>

<p>
In the stopGame() method, we kill the timer and set the appropriate flags. 
</p>

<pre class="explanation">
 if (ball->getRect().bottom() > 400)
   stopGame(); 
</pre>

<p>
If the ball hits the bottom, we stop the game. 
</p>


<pre class="explanation">
 for (int i=0, j=0; i<30; i++) {
   if (bricks[i]->isDestroyed()) {
     j++;
   }
   if (j==30) 
     victory();
 }
</pre>

<p>
We check how many bricks are destroyed. If we destroyed all 30 bricks, we win the game. 
</p>

<pre class="explanation">
 if (ballLPos < first) {
   ball->setXDir(-1);
   ball->setYDir(-1);
 }
</pre>

<p>
If the ball hits the first part of the paddle, we change the direction of the ball to north east. 
</p>

<pre class="explanation">
 if(bricks[i]->getRect().contains(pointTop)) {
   ball->setYDir(1);
 }
</pre>

<p>
If the ball hits the bottom of the brick, we change the y direction of the ball. It goes down. 
</p>

<div class="codehead">main.cpp</div>
<pre class="code">
#include "breakout.h"
#include &lt;QDesktopWidget&gt;
#include &lt;QApplication&gt;

void center(QWidget &amp;widget)
{
  int x, y;
  int screenWidth;
  int screenHeight;

  int WIDTH = 300;
  int HEIGHT = 400;
  

  QDesktopWidget *desktop = QApplication::desktop();

  screenWidth = desktop->width();
  screenHeight = desktop->height();
 
  x = (screenWidth - WIDTH) / 2;
  y = (screenHeight - HEIGHT) / 2;

  widget.setGeometry(x, y, WIDTH, HEIGHT);
  widget.setFixedSize(WIDTH, HEIGHT);
}


int main(int argc, char *argv[])
{
  QApplication app(argc, argv);  
    
  Breakout window;
  
  window.setWindowTitle("Breakout");
  window.show();
  center(window);

  return app.exec();
}
</pre>

<p>
Main file.
</p>


<img src="/img/gui/qt4/breakout.png" alt="The Breakout game">
<div class="figure">Figure: The Breakout game</div>

<p>
So this was the Breakout game in C++ and Qt4 library. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div>
<br>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified December 22, 2011  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content  -->

</div> <!-- container  -->

</body>
</html>

